package IC.Parser;

%%

%cup
%class Lexer
%public
%function next_token
%type Token
%line
%scanerror LexicalError

%{
	StringBuilder _stringBuilder = new StringBuilder();
  
	/*Non zero based value of yyline*/
	private int getNZBLine() {
		return yyline + 1;		
	}
%}

%eofval{
	if (yystate() == STRING_STATE) {
		throw new LexicalError("unterminated string literal.", getNZBLine());
	}
	
	if (yystate() == COMMENT_STATE || yystate() == COMMENT_STAR_STATE) {
		throw new LexicalError("unterminated multi-line comment", getNZBLine());
	}
	
	return new Token(sym.EOF, getNZBLine());
	
%eofval}

STRING_CHAR = ([ !#-\[\]-~]|\\\\|\\\"|\\t|\\n)

%state STRING_STATE, COMMENT_STATE, COMMENT_STAR_STATE

%%


<YYINITIAL> {

	/* keywords */
	"class" 			{ return new Token(sym.CLASS, getNZBLine()); }
	"extends" 			{ return new Token(sym.EXTENDS, getNZBLine()); }
	"static"  			{ return new Token(sym.STATIC, getNZBLine()); }
	"void" 				{ return new Token(sym.VOID, getNZBLine()); }
	"int" 				{ return new Token(sym.INT, getNZBLine()); }
	"boolean" 			{ return new Token(sym.BOOLEAN, getNZBLine()); }
	"string" 			{ return new Token(sym.STRING, getNZBLine()); }
	"return" 			{ return new Token(sym.RETURN, getNZBLine()); }
	"if" 				{ return new Token(sym.IF, getNZBLine()); }
	"else" 				{ return new Token(sym.ELSE, getNZBLine()); }
	"while" 			{ return new Token(sym.WHILE, getNZBLine()); }
	"break" 			{ return new Token(sym.BREAK, getNZBLine()); }
	"continue" 			{ return new Token(sym.CONTINUE, getNZBLine()); }
	"this" 				{ return new Token(sym.THIS, getNZBLine()); }
	"new" 				{ return new Token(sym.NEW, getNZBLine()); }
	"length" 			{ return new Token(sym.LENGTH, getNZBLine()); }
	"true" 				{ return new Token(sym.TRUE, getNZBLine()); }
	"false" 			{ return new Token(sym.FALSE, getNZBLine()); }
	"null" 				{ return new Token(sym.NULL, getNZBLine()); }
	
	/* seperators */
	"," 				{ return new Token(sym.COMMA, getNZBLine()); }
	"." 				{ return new Token(sym.DOT, getNZBLine()); }
	"[" 				{ return new Token(sym.LB, getNZBLine()); }
	"]" 				{ return new Token(sym.RB, getNZBLine()); }
	"(" 				{ return new Token(sym.LP,getNZBLine()); }
	")" 				{ return new Token(sym.RP,getNZBLine()); }
	"{" 				{ return new Token(sym.LCBR, getNZBLine()); }
	"}" 				{ return new Token(sym.RCBR, getNZBLine()); }
	
	/* operators */
	"=" 				{ return new Token(sym.ASSIGN, getNZBLine()); }
	"/" 				{ return new Token(sym.DIVIDE, getNZBLine()); }
	"==" 				{ return new Token(sym.EQUAL, getNZBLine()); }
	">" 				{ return new Token(sym.GT, getNZBLine()); }
	">=" 				{ return new Token(sym.GTE, getNZBLine()); }
	"&&" 				{ return new Token(sym.LAND, getNZBLine()); }
	"!" 				{ return new Token(sym.LNEG, getNZBLine()); }
	"||" 				{ return new Token(sym.LOR, getNZBLine()); }
	"<" 				{ return new Token(sym.LT, getNZBLine()); }
	"<=" 				{ return new Token(sym.LTE, getNZBLine()); }
	"-" 				{ return new Token(sym.MINUS, getNZBLine()); }
	"%" 				{ return new Token(sym.MOD, getNZBLine()); }
	"*" 				{ return new Token(sym.MULTIPLY, getNZBLine()); }
	"!=" 				{ return new Token(sym.NEQUAL, getNZBLine()); }
	"+" 				{ return new Token(sym.PLUS, getNZBLine()); }
	";" 				{ return new Token(sym.SEMI, getNZBLine()); }

	/* whitespace */
	[ \r\n\t] 			{ /*ignore*/ }
	
	/* identifiers */
	[a-z][0-9_a-zA-Z]* 	{ return new Token(sym.ID, getNZBLine(), yytext()); } 
	[A-Z][0-9_a-zA-Z]* 	{ return new Token(sym.CLASS_ID, getNZBLine(), yytext()); }
	
	/* integer literal */
	(0+|[1-9][0-9]*) 	{ 
							long value = 0;
							boolean rangeError = false;
							try {
								value = Long.parseLong(yytext());
							}
							catch(NumberFormatException e) {
								rangeError = true;
							}
							
							// check if greater than 2^31
							if (value > 0x80000000L) {
								rangeError = true;
							}
							
							if (rangeError) {
								throw new LexicalError("Integer literal not in range: '" + yytext() + "'", getNZBLine());
							}
							
							return new Token(sym.INTEGER, getNZBLine(), value);
						}
	
	0+[1-9][0-9]* 					{ throw new LexicalError("Integer literal has leading zeros: '" + yytext() + "'", getNZBLine());	}
	[0-9]+[_a-zA-Z][0-9_a-zA-Z]* 	{ throw new LexicalError("Identifier starts with a number: '" + yytext() + "'", getNZBLine());	}
	
	/*string literal */
	\"					{ 
							_stringBuilder.setLength(0);
							_stringBuilder.append("\"");
							yybegin(STRING_STATE); 
						} 
	
	
	
	/* comments */
	"//"[^\n]*			{ /*ignore*/ }
	"/*"				{ yybegin(COMMENT_STATE); }
	
	. 					{ throw new LexicalError("illegal character '" + yytext() + "'", getNZBLine()); }
	
}

<STRING_STATE> {
	{STRING_CHAR}+ 		{ _stringBuilder.append(yytext()); }
	\" 					{
							yybegin(YYINITIAL);
							_stringBuilder.append("\"");
							return new Token(sym.QUOTE, getNZBLine(), _stringBuilder.toString()); 
						}
	.|\n				{ throw new LexicalError("illegal character in string: '" + yytext() + "'", getNZBLine()); }
}

<COMMENT_STATE> {
	\*+						{ yybegin(COMMENT_STAR_STATE); }
	[^\*]+					{ /* ignore */ }
}

<COMMENT_STAR_STATE> {
	"/"						{ yybegin(YYINITIAL); }
	"*"						{ /* ignore */ }
	[^\*]					{ yybegin(COMMENT_STATE); }								
}
